package com.xiang.mall.framework.core.result

import com.fasterxml.jackson.databind.ObjectMapper
import com.xiang.mall.framework.base.constant.ErrorStatus
import com.xiang.mall.framework.base.protocol.ApiResult
import org.springframework.core.MethodParameter
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.http.server.ServletServerHttpResponse
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.*
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice

/**
 * @author xiangliyou
 * @email xiangliyou1814@mail.yneusoft.com
 * @date 2019-03-06 15:04
 * 同一处理返回结果,如果使用了自动生成 api 文档的工具(swagger),需要配置不处理它返回的结果,处理后返回的结果格式变了,它对应的前端识别不了
 */
@ControllerAdvice(annotations = [RestController::class, Controller::class])
class ResultHandler(
    private val mapper: ObjectMapper
) : ResponseBodyAdvice<Any> {

    /**
     * 所有请求使用的注解
     */
    private val REQUEST_METHOD = listOf(
        RequestMapping::class.java,
        GetMapping::class.java,
        PostMapping::class.java,
        DeleteMapping::class.java,
        PutMapping::class.java
    )

    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
        val annotation = returnType.annotatedElement
        //所有 controller 方法标注了请求的注解,都需要对返回结果进行处理
        return REQUEST_METHOD.any { annotation.isAnnotationPresent(it) }
    }

    override fun beforeBodyWrite(
        body: Any?, returnType: MethodParameter, selectedContentType: MediaType,
        selectedConverterType: Class<out HttpMessageConverter<*>>,
        request: ServerHttpRequest, response: ServerHttpResponse
    ): Any? {
        val resp = response as ServletServerHttpResponse
        return when (body) {
            is ApiResult<*> -> body
            is String -> mapper.writeValueAsString(ApiResult.buildSuccess(data = body)).toString()
            is ErrorStatus -> return ApiResult.buildFailure(status = body, data = "")
            else -> {
                if (resp.servletResponse.status == HttpStatus.OK.value()) {
                    ApiResult.buildSuccess(data = body)
                } else {
                    ApiResult.buildFailure(code = resp.servletResponse.status, data = body)
                }
            }
        }
    }
}